home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Files / Standard File / StandardGetFolder / MoreDesktopMgr.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  32.2 KB  |  1,256 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        MoreDesktopMgr.c
  3.     
  4.     Description:A collection of useful high-level Desktop Manager routines.
  5.                 If the Desktop Manager isn't available, use the Desktop file
  6.                 for 'read' operations.
  7.                 We do more because we can...
  8.  
  9.     Author:        JL & NG
  10.  
  11.     Copyright:     Copyright: © 1992-1999 by Apple Computer, Inc.
  12.                 all rights reserved.
  13.     
  14.     Disclaimer:    You may incorporate this sample code into your applications without
  15.                 restriction, though the sample code has been provided "AS IS" and the
  16.                 responsibility for its operation is 100% yours.  However, what you are
  17.                 not permitted to do is to redistribute the source as "DSC Sample Code"
  18.                 after having made changes. If you're going to re-distribute the source,
  19.                 we require that you make it clear in the source that the code was
  20.                 descended from Apple Sample Code, but that you've made changes.
  21.     
  22.     Change History (most recent first):
  23.                 6/25/99    Updated for Metrowerks Codewarror Pro 2.1(KG)
  24. */
  25.  
  26.  
  27. #include <Types.h>
  28. #include <Errors.h>
  29. #include <Memory.h>
  30. #include <Files.h>
  31. #include <Resources.h>
  32. #include <Icons.h>
  33.  
  34. #define    __COMPILINGMOREFILES
  35.  
  36. #include "MoreFiles.h"
  37. #include "MoreFilesExtras.h"
  38. #include "Search.h"
  39. #include "MoreDesktopMgr.h"
  40.  
  41. /*****************************************************************************/
  42.  
  43. /*    Desktop file notes:
  44. **
  45. **    •    The Desktop file is owned by the Finder and is normally open by the
  46. **        Finder. That means that we only have read-only access to the Desktop
  47. **        file.
  48. **    •    Since the Resource Manager doesn't support shared access to resource
  49. **        files and we're using read-only access, we don't ever leave the
  50. **        Desktop file open.  We open a path to it, get the data we want out
  51. **        of it, and then close the open path. This is the only safe way to
  52. **        open a resource file with read-only access since some other program
  53. **        could have it open with write access.
  54. **    •    The bundle related resources in the Desktop file are normally
  55. **        purgable, so when we're looking through them, we don't bother to
  56. **        release resources we're done looking at - closing the resource file
  57. **        (which we always do) will release them.
  58. **    •    Since we can't assume the Desktop file is named "Desktop"
  59. **        (it probably is everywhere but France), we get the Desktop
  60. **        file's name by searching the volume's root directory for a file
  61. **        with fileType == 'FNDR' and creator == 'ERIK'. The only problem with
  62. **        this scheme is that someone could create another file with that type
  63. **        and creator in the root directory and we'd find the wrong file.
  64. **        The chances of this are very slim.
  65. */
  66.  
  67. /*****************************************************************************/
  68.  
  69. /* local defines */
  70.  
  71. enum
  72. {
  73.     kBNDLResType    = 'BNDL',
  74.     kFREFResType    = 'FREF',
  75.     kIconFamResType    = 'ICN#',
  76.     kFCMTResType    = 'FCMT',
  77.     kAPPLResType    = 'APPL'
  78. };
  79.  
  80. /*****************************************************************************/
  81.  
  82. /* local data structures */
  83.  
  84. #if PRAGMA_ALIGN_SUPPORTED
  85. #pragma options align=mac68k
  86. #endif
  87.  
  88. struct IDRec
  89. {
  90.     short        localID;
  91.     short        rsrcID;
  92. };
  93. typedef struct IDRec IDRec;
  94. typedef    IDRec *IDRecPtr;
  95.  
  96. struct BundleType
  97. {
  98.     OSType        type;            /* 'ICN#' or 'FREF' */
  99.     short        count;            /* number of IDRecs - 1 */
  100.     IDRec        idArray[1];
  101. };
  102. typedef struct BundleType BundleType;
  103. typedef BundleType *BundleTypePtr;
  104.  
  105. struct BNDLRec
  106. {
  107.     OSType        signature;        /* creator type signature */
  108.     short        versionID;        /* version - should always be 0 */
  109.     short        numTypes;        /* number of elements in typeArray - 1 */
  110.     BundleType    typeArray[1];
  111. };
  112. typedef struct BNDLRec BNDLRec;
  113. typedef BNDLRec **BNDLRecHandle;
  114.  
  115. struct FREFRec
  116. {
  117.     OSType        fileType;        /* file type */
  118.     short        iconID;            /* icon local ID */
  119.     Str255        fileName;        /* file name */
  120. };
  121. typedef struct FREFRec FREFRec;
  122. typedef FREFRec **FREFRecHandle;
  123.  
  124. struct APPLRec
  125. {
  126.     OSType        creator;        /* creator type signature */
  127.     long        parID;            /* parent directory ID */
  128.     Str255        applName;        /* application name */
  129. };
  130. typedef struct APPLRec APPLRec;
  131. typedef APPLRec *APPLRecPtr;
  132.  
  133. #if PRAGMA_ALIGN_SUPPORTED
  134. #pragma options align=reset
  135. #endif
  136.  
  137. /*****************************************************************************/
  138.  
  139. /* static prototypes */
  140.  
  141. static    OSErr    GetDesktopFileName(short vRefNum,
  142.                                    Str255 desktopName);
  143.  
  144. static    OSErr    GetAPPLFromDesktopFile(ConstStr255Param volName,
  145.                                        short vRefNum,
  146.                                        OSType creator,
  147.                                        short *applVRefNum,
  148.                                        long *applParID,
  149.                                        Str255 applName);
  150.  
  151. static    OSErr    FindBundleGivenCreator(OSType creator,
  152.                                        BNDLRecHandle *returnBndl);
  153.                                        
  154. static    OSErr    FindTypeInBundle(OSType typeToFind,
  155.                                  BNDLRecHandle theBndl,
  156.                                  BundleTypePtr *returnBundleType);
  157.                                          
  158. static    OSErr    GetLocalIDFromFREF(BundleTypePtr theBundleType,
  159.                                    OSType fileType,
  160.                                    short *iconLocalID);
  161.  
  162. static    OSErr    GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType,
  163.                                          short iconLocalID,
  164.                                          short *iconRsrcID);
  165.  
  166. static    OSType    DTIconToResIcon(short iconType);
  167.  
  168. static    OSErr    GetIconFromDesktopFile(ConstStr255Param volName,
  169.                                        short vRefNum,
  170.                                        short iconType,
  171.                                        OSType fileCreator,
  172.                                        OSType fileType,
  173.                                        Handle *iconHandle);
  174.  
  175. static    OSErr    GetCommentID(short vRefNum,
  176.                              long dirID,
  177.                              ConstStr255Param name,
  178.                              short *commentID);
  179.  
  180. static    OSErr    GetCommentFromDesktopFile(short vRefNum,
  181.                                           long dirID,
  182.                                           ConstStr255Param name,
  183.                                           Str255 comment);
  184.  
  185. /*****************************************************************************/
  186.  
  187. /*
  188. **    GetDesktopFileName
  189. **
  190. **    Get the name of the Desktop file.
  191. */
  192. static    OSErr    GetDesktopFileName(short vRefNum,
  193.                                    Str255 desktopName)
  194. {
  195.     OSErr            error;
  196.     HParamBlockRec    pb;
  197.     short            index;
  198.     Boolean            found;
  199.     
  200.     pb.fileParam.ioNamePtr = desktopName;
  201.     pb.fileParam.ioVRefNum = vRefNum;
  202.     pb.fileParam.ioFVersNum = 0;
  203.     index = 1;
  204.     found = false;
  205.     do
  206.     {
  207.         pb.fileParam.ioDirID = fsRtDirID;
  208.         pb.fileParam.ioFDirIndex = index;
  209.         error = PBHGetFInfoSync(&pb);
  210.         if ( error == noErr )
  211.         {
  212.             if ( (pb.fileParam.ioFlFndrInfo.fdType == 'FNDR') &&
  213.                  (pb.fileParam.ioFlFndrInfo.fdCreator == 'ERIK') )
  214.             {
  215.                 found = true;
  216.             }
  217.         }
  218.         ++index;
  219.     } while ( (error == noErr) && !found );
  220.     
  221.     return ( error );
  222. }
  223.  
  224. /*****************************************************************************/
  225.  
  226. pascal    OSErr    DTOpen(ConstStr255Param volName,
  227.                        short vRefNum,
  228.                        short *dtRefNum,
  229.                        Boolean *newDTDatabase)
  230. {
  231.     OSErr error;
  232.     GetVolParmsInfoBuffer volParmsInfo;
  233.     long infoSize;
  234.     DTPBRec pb;
  235.     
  236.     /* Check for volume Desktop Manager support before calling */
  237.     infoSize = sizeof(GetVolParmsInfoBuffer);
  238.     error = HGetVolParms(volName, vRefNum, &volParmsInfo, &infoSize);
  239.     if ( error == noErr )
  240.     {
  241.         if ( hasDesktopMgr(volParmsInfo) )
  242.         {
  243.             pb.ioNamePtr = (StringPtr)volName;
  244.             pb.ioVRefNum = vRefNum;
  245.             error = PBDTOpenInform(&pb);
  246.             /* PBDTOpenInform informs us if the desktop was just created */
  247.             /* by leaving the low bit of ioTagInfo clear (0) */
  248.             *newDTDatabase = ((pb.ioTagInfo & 1L) == 0);
  249.             if ( error == paramErr )
  250.             {
  251.                 error = PBDTGetPath(&pb);
  252.                 /* PBDTGetPath doesn't tell us if the database is new */
  253.                 /* so assume it is not new */
  254.                 *newDTDatabase = false;
  255.             }
  256.             *dtRefNum = pb.ioDTRefNum;
  257.         }
  258.         else
  259.         {
  260.             error = paramErr;
  261.         }
  262.     }
  263.     return ( error );
  264. }
  265.  
  266. /*****************************************************************************/
  267.  
  268. /*
  269. **    GetAPPLFromDesktopFile
  270. **
  271. **    Get a application's location from the
  272. **    Desktop file's 'APPL' resources.
  273. */
  274. static    OSErr    GetAPPLFromDesktopFile(ConstStr255Param volName,
  275.                                        short vRefNum,
  276.                                        OSType creator,
  277.                                        short *applVRefNum,
  278.                                        long *applParID,
  279.                                        Str255 applName)
  280. {
  281.     OSErr error;
  282.     short realVRefNum;
  283.     Str255 desktopName;
  284.     short savedResFile;
  285.     short dfRefNum;
  286.     Handle applResHandle;
  287.     Boolean foundCreator;
  288.     Ptr applPtr;
  289.     long applSize;
  290.     
  291.     error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
  292.     if ( error == noErr )
  293.     {
  294.         error = GetDesktopFileName(realVRefNum, desktopName);
  295.         if ( error == noErr )
  296.         {
  297.             savedResFile = CurResFile();
  298.             /*
  299.             **    Open the 'Desktop' file in the root directory. (because
  300.             **    opening the resource file could preload unwanted resources,
  301.             **    bracket the call with SetResLoad(s))
  302.             */
  303.             SetResLoad(false);
  304.             dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
  305.             SetResLoad(true);
  306.             
  307.             if ( dfRefNum != -1)
  308.             {
  309.                 /* Get 'APPL' resource ID 0 */
  310.                 applResHandle = Get1Resource(kAPPLResType, 0);
  311.                 if ( applResHandle != NULL )
  312.                 {
  313.                     applSize = InlineGetHandleSize((Handle)applResHandle);
  314.                     if ( applSize != 0 )    /* make sure the APPL resource isn't empty */
  315.                     {
  316.                         foundCreator = false;
  317.                         applPtr = *applResHandle;
  318.                         
  319.                         /* APPL's don't have a count so I have to use the size as the bounds */
  320.                         while ( (foundCreator == false) &&
  321.                                 (applPtr < (*applResHandle + applSize)) )
  322.                         {
  323.                             if ( ((APPLRecPtr)applPtr)->creator == creator )
  324.                             {
  325.                                 foundCreator = true;
  326.                             }
  327.                             else
  328.                             {
  329.                                 /* fun with pointer math... */
  330.                                 applPtr += sizeof(OSType) +
  331.                                            sizeof(long) +
  332.                                            ((APPLRecPtr)applPtr)->applName[0] + 1;
  333.                                 /* application mappings are word aligned within the resource */
  334.                                 if ( ((unsigned long)applPtr % 2) != 0 )
  335.                                 {
  336.                                     applPtr += 1;
  337.                                 }
  338.                             }
  339.                         }
  340.                         if ( foundCreator == true )
  341.                         {
  342.                             *applVRefNum = realVRefNum;
  343.                             *applParID = ((APPLRecPtr)applPtr)->parID;
  344.                             BlockMoveData(((APPLRecPtr)applPtr)->applName,
  345.                                           applName,
  346.                                           ((APPLRecPtr)applPtr)->applName[0] + 1);
  347.                             /* error is already noErr */
  348.                         }
  349.                         else
  350.                         {
  351.                             error = afpItemNotFound;    /* didn't find a creator match */
  352.                         }
  353.                     }
  354.                     else
  355.                     {
  356.                         error = afpItemNotFound;    /* no APPL mapping available */
  357.                     }
  358.                 }
  359.                 else
  360.                 {
  361.                     error = afpItemNotFound;    /* no APPL mapping available */
  362.                 }
  363.                 
  364.                 /* restore the resource chain and close the Desktop file */
  365.                 UseResFile(savedResFile);
  366.                 CloseResFile(dfRefNum);
  367.             }
  368.             else
  369.             {
  370.                 error = afpItemNotFound;
  371.             }
  372.         }
  373.     }
  374.     
  375.     return ( error );
  376. }
  377.  
  378. /*****************************************************************************/
  379.  
  380. pascal    OSErr    DTXGetAPPL(ConstStr255Param volName,
  381.                            short vRefNum,
  382.                            OSType creator,
  383.                            Boolean searchCatalog,
  384.                            short *applVRefNum,
  385.                            long *applParID,
  386.                            Str255 applName)
  387. {
  388.     OSErr error;
  389.     UniversalFMPB pb;
  390.     short dtRefNum;
  391.     Boolean newDTDatabase;
  392.     short realVRefNum;
  393.     short index;
  394.     Boolean applFound;
  395.     FSSpec spec;
  396.     long actMatchCount;
  397.     
  398.     /* get the real vRefNum */
  399.     error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
  400.     if ( error == noErr )
  401.     {
  402.         error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase);
  403.         if ( error == noErr )
  404.         {
  405.             if ( !newDTDatabase )
  406.             {
  407.                 index = 0;
  408.                 applFound = false;
  409.                 do
  410.                 {
  411.                     pb.dtPB.ioNamePtr = applName;
  412.                     pb.dtPB.ioDTRefNum = dtRefNum;
  413.                     pb.dtPB.ioIndex = index;
  414.                     pb.dtPB.ioFileCreator = creator;
  415.                     error = PBDTGetAPPLSync(&pb.dtPB);
  416.                     if ( error == noErr )
  417.                     {
  418.                         /* got a match - see if it is valid */
  419.                         
  420.                         *applVRefNum = realVRefNum; /* get the vRefNum now */
  421.                         *applParID = pb.dtPB.ioAPPLParID; /* get the parent ID now */
  422.     
  423.                         /* pb.hPB.fileParam.ioNamePtr is already set */
  424.                         pb.hPB.fileParam.ioVRefNum = realVRefNum;
  425.                         pb.hPB.fileParam.ioFVersNum = 0;
  426.                         pb.hPB.fileParam.ioDirID = *applParID;
  427.                         pb.hPB.fileParam.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  428.                         if ( PBHGetFInfoSync(&pb.hPB) == noErr )
  429.                         {
  430.                             if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator == creator) &&
  431.                                  (pb.hPB.fileParam.ioFlFndrInfo.fdType == 'APPL') )
  432.                             {
  433.                                 applFound = true;
  434.                             }
  435.                         }
  436.                     }
  437.                     ++index;
  438.                 } while ( (error == noErr) && !applFound );
  439.                 if ( error != noErr )
  440.                 {
  441.                     error = afpItemNotFound;
  442.                 }
  443.             }
  444.             else
  445.             {
  446.                 /* Desktop database is empty (new), set error to try CatSearch */
  447.                 error = afpItemNotFound;
  448.             }
  449.         }
  450.         /* acceptable errors from Desktop Manager to continue are paramErr or afpItemNotFound */
  451.         if ( error == paramErr )
  452.         {
  453.             /* if paramErr, the volume didn't support the Desktop Manager */
  454.             /* try the Desktop file */
  455.             
  456.             error = GetAPPLFromDesktopFile(volName, vRefNum, creator,
  457.                                             applVRefNum, applParID, applName);
  458.             if ( error == noErr )
  459.             {
  460.                 /* got a match - see if it is valid */
  461.                 
  462.                 pb.hPB.fileParam.ioNamePtr = applName;
  463.                 pb.hPB.fileParam.ioVRefNum = *applVRefNum;
  464.                 pb.hPB.fileParam.ioFVersNum = 0;
  465.                 pb.hPB.fileParam.ioDirID = *applParID;
  466.                 pb.hPB.fileParam.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  467.                 if ( PBHGetFInfoSync(&pb.hPB) == noErr )
  468.                 {
  469.                     if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator != creator) ||
  470.                          (pb.hPB.fileParam.ioFlFndrInfo.fdType != 'APPL') )
  471.                     {
  472.                         error = afpItemNotFound;
  473.                     }
  474.                 }
  475.                 else if ( error == fnfErr )
  476.                 {
  477.                     error = afpItemNotFound;
  478.                 }
  479.             }
  480.         }
  481.         /* acceptable error from DesktopFile code to continue is afpItemNotFound */
  482.         if ( (error == afpItemNotFound) && searchCatalog)
  483.         {
  484.             /* Couldn't be found in the Desktop file either, */
  485.             /* try searching with CatSearch if requested */
  486.             
  487.             error = CreatorTypeFileSearch(NULL, realVRefNum, creator, kAPPLResType, &spec, 1,
  488.                                             &actMatchCount, true);
  489.             if ( (error == noErr) || (error == eofErr) )
  490.             {
  491.                 if ( actMatchCount > 0 )
  492.                 {
  493.                     *applVRefNum = spec.vRefNum;
  494.                     *applParID = spec.parID;
  495.                     BlockMoveData(spec.name, applName, spec.name[0] + 1);
  496.                 }
  497.                 else
  498.                 {
  499.                     error = afpItemNotFound;
  500.                 }
  501.             }
  502.         }
  503.     }
  504.     
  505.     return ( error );
  506. }
  507.  
  508. /*****************************************************************************/
  509.  
  510. pascal    OSErr    FSpDTXGetAPPL(ConstStr255Param volName,
  511.                               short vRefNum,
  512.                               OSType creator,
  513.                               Boolean searchCatalog,
  514.                               FSSpec *spec)
  515. {
  516.     return ( DTXGetAPPL(volName, vRefNum, creator, searchCatalog,
  517.                         &(spec->vRefNum), &(spec->parID), spec->name) );
  518. }
  519.  
  520. /*****************************************************************************/
  521.  
  522. pascal    OSErr    DTGetAPPL(ConstStr255Param volName,
  523.                           short vRefNum,
  524.                           OSType creator,
  525.                           short *applVRefNum,
  526.                           long *applParID,
  527.                           Str255 applName)
  528. {
  529.     /* Call DTXGetAPPL with the "searchCatalog" parameter true */ 
  530.     return ( DTXGetAPPL(volName, vRefNum, creator, true,
  531.                         applVRefNum, applParID, applName) );
  532. }
  533.  
  534. /*****************************************************************************/
  535.  
  536. pascal    OSErr    FSpDTGetAPPL(ConstStr255Param volName,
  537.                              short vRefNum,
  538.                              OSType creator,
  539.                              FSSpec *spec)
  540. {
  541.     /* Call DTXGetAPPL with the "searchCatalog" parameter true */ 
  542.     return ( DTXGetAPPL(volName, vRefNum, creator, true,
  543.                         &(spec->vRefNum), &(spec->parID), spec->name) );
  544. }
  545.  
  546. /*****************************************************************************/
  547.  
  548. /*
  549. **    FindBundleGivenCreator
  550. **
  551. **    Search the current resource file for the 'BNDL' resource with the given
  552. **    creator and return a handle to it.
  553. */
  554. static    OSErr    FindBundleGivenCreator(OSType creator,
  555.                                        BNDLRecHandle *returnBndl)
  556. {
  557.     OSErr            error;
  558.     short            numOfBundles;
  559.     short            index;
  560.     BNDLRecHandle    theBndl;
  561.     
  562.     error = afpItemNotFound;    /* default to not found */
  563.     
  564.     /* Search each BNDL resource until we find the one with a matching creator. */
  565.     
  566.     numOfBundles = Count1Resources(kBNDLResType);
  567.     index = 1;
  568.     *returnBndl = NULL;
  569.     
  570.     while ( (index <= numOfBundles) && (*returnBndl == NULL) )
  571.     {
  572.         theBndl = (BNDLRecHandle)Get1IndResource(kBNDLResType, index);
  573.         
  574.         if ( theBndl != NULL )
  575.         {
  576.             if ( (*theBndl)->signature == creator )
  577.             {
  578.                 /* numTypes and typeArray->count will always be the actual count minus 1, */
  579.                 /* so 0 in both fields is valid. */
  580.                 if ( ((*theBndl)->numTypes >= 0) && ((*theBndl)->typeArray->count >= 0) )
  581.                 {
  582.                     /* got it */
  583.                     *returnBndl = theBndl;
  584.                     error = noErr;
  585.                 }
  586.             }
  587.         }    
  588.         
  589.         index ++;
  590.     }
  591.     
  592.     return ( error );
  593. }
  594.  
  595. /*****************************************************************************/
  596.  
  597. /*
  598. **    FindTypeInBundle
  599. **
  600. **    Given a Handle to a BNDL return a pointer to the desired type
  601. **    in it. If the type is not found, or if the type's count < 0,
  602. **    return afpItemNotFound.
  603. */
  604. static    OSErr    FindTypeInBundle(OSType typeToFind,
  605.                                  BNDLRecHandle theBndl,
  606.                                  BundleTypePtr *returnBundleType)
  607. {
  608.     OSErr            error;
  609.     short            index;
  610.     Ptr                ptrIterator;    /* use a Ptr so we can do ugly pointer math */
  611.     
  612.     error = afpItemNotFound;    /* default to not found */
  613.     
  614.     ptrIterator = (Ptr)((*theBndl)->typeArray);
  615.     index = 0;
  616.     *returnBundleType = NULL;
  617.  
  618.     while ( (index < ((*theBndl)->numTypes + 1)) &&
  619.             (*returnBundleType == NULL) )
  620.     {
  621.         if ( (((BundleTypePtr)ptrIterator)->type == typeToFind) &&
  622.              (((BundleTypePtr)ptrIterator)->count >= 0) )
  623.         {
  624.                 *returnBundleType = (BundleTypePtr)ptrIterator;
  625.                 error = noErr;
  626.         }
  627.         else
  628.         {
  629.             ptrIterator += ( sizeof(OSType) +
  630.                              sizeof(short) +
  631.                              ( sizeof(IDRec) * (((BundleTypePtr)ptrIterator)->count + 1) ) );
  632.             ++index;
  633.         }
  634.     }
  635.         
  636.     return ( error );
  637. }
  638.  
  639. /*****************************************************************************/
  640.  
  641. /*
  642. **    GetLocalIDFromFREF
  643. **
  644. **    Given a pointer to a 'FREF' BundleType record, load each 'FREF' resource
  645. **    looking for a matching fileType. If a matching fileType is found, return
  646. **    its icon local ID. If no match is found, return afpItemNotFound as the
  647. **    function result.
  648. */
  649. static    OSErr    GetLocalIDFromFREF(BundleTypePtr theBundleType,
  650.                                    OSType fileType,
  651.                                    short *iconLocalID)
  652. {
  653.     OSErr            error;
  654.     short            index;
  655.     IDRecPtr        idIterator;
  656.     FREFRecHandle    theFref;
  657.     
  658.     error = afpItemNotFound;    /* default to not found */
  659.     
  660.     /* For each localID in this type, get the FREF resource looking for fileType */
  661.     index = 0;
  662.     idIterator = &theBundleType->idArray[0];
  663.     *iconLocalID = 0;
  664.     
  665.     while ( (index <= theBundleType->count) && (*iconLocalID == 0) )
  666.     {
  667.         theFref = (FREFRecHandle)Get1Resource(kFREFResType, idIterator->rsrcID);
  668.         if ( theFref != NULL )
  669.         {
  670.             if ( (*theFref)->fileType == fileType )
  671.             {
  672.                 *iconLocalID = (*theFref)->iconID;
  673.                 error = noErr;
  674.             }
  675.         }
  676.         
  677.         ++idIterator;
  678.         ++index;
  679.     }
  680.     
  681.     return ( error );
  682. }
  683.  
  684. /*****************************************************************************/
  685.  
  686. /*
  687. **    GetIconRsrcIDFromLocalID
  688. **
  689. **    Given a pointer to a 'ICN#' BundleType record, look for the IDRec with
  690. **    the localID that matches iconLocalID. If a matching IDRec is found,
  691. **    return the IDRec's rsrcID field value. If no match is found, return
  692. **    afpItemNotFound as the function result.
  693. */
  694. static    OSErr    GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType,
  695.                                          short iconLocalID,
  696.                                          short *iconRsrcID)
  697. {
  698.     OSErr        error;
  699.     short        index;
  700.     IDRecPtr    idIterator;
  701.     
  702.     error = afpItemNotFound;    /* default to not found */
  703.     
  704.     /* Find the rsrcID of the icon family type, given the localID */
  705.     index = 0;
  706.     idIterator = &theBundleType->idArray[0];
  707.     *iconRsrcID = 0;
  708.     
  709.     while ( (index <= theBundleType->count) && (*iconRsrcID == 0) )
  710.     {
  711.         if ( idIterator->localID == iconLocalID )
  712.         {
  713.             *iconRsrcID = idIterator->rsrcID;
  714.             error = noErr;
  715.         }
  716.         
  717.         idIterator ++;
  718.         index ++;
  719.     }
  720.     
  721.     return ( error );
  722. }
  723.  
  724. /*****************************************************************************/
  725.  
  726. /*
  727. **    DTIconToResIcon
  728. **
  729. **    Map a Desktop Manager icon type to the corresponding resource type.
  730. **    Return (OSType)0 if there is no corresponding resource type.
  731. */
  732. static    OSType    DTIconToResIcon(short iconType)
  733. {
  734.     OSType    resType;
  735.     
  736.     switch ( iconType )
  737.     {
  738.         case kLargeIcon:
  739.             resType = large1BitMask;
  740.             break;
  741.         case kLarge4BitIcon:
  742.             resType = large4BitData;
  743.             break;
  744.         case kLarge8BitIcon:
  745.             resType = large8BitData;
  746.             break;
  747.         case kSmallIcon:
  748.             resType = small1BitMask;
  749.             break;
  750.         case kSmall4BitIcon:
  751.             resType = small4BitData;
  752.             break;
  753.         case kSmall8BitIcon:
  754.             resType = small8BitData;
  755.             break;
  756.         default:
  757.             resType = (OSType)0;
  758.             break;
  759.     }
  760.     
  761.     return ( resType );
  762. }
  763.  
  764. /*****************************************************************************/
  765.  
  766. /*
  767. **    GetIconFromDesktopFile
  768. **
  769. **    INPUT a pointer to a non-existent Handle, because we'll allocate one
  770. **
  771. **    search each BNDL resource for the right fileCreator and once we get it
  772. **        find the 'FREF' type in BNDL
  773. **        for each localID in the type, open the FREF resource
  774. **            if the FREF is the desired fileType
  775. **                get its icon localID
  776. **                get the ICN# type in BNDL
  777. **                get the icon resource number from the icon localID
  778. **                get the icon resource type from the desktop mgr's iconType
  779. **                get the icon of that type and number
  780. */
  781. static    OSErr    GetIconFromDesktopFile(ConstStr255Param volName,
  782.                                        short vRefNum,
  783.                                        short iconType,
  784.                                        OSType fileCreator,
  785.                                        OSType fileType,
  786.                                        Handle *iconHandle)
  787. {
  788.     OSErr            error;
  789.     short            realVRefNum;
  790.     Str255            desktopName;
  791.     short            savedResFile;
  792.     short            dfRefNum;
  793.     BNDLRecHandle    theBndl = NULL;
  794.     BundleTypePtr    theBundleType;
  795.     short            iconLocalID;
  796.     short            iconRsrcID;
  797.     OSType            iconRsrcType;
  798.     Handle            returnIconHandle;    
  799.     char            bndlState;
  800.     
  801.     *iconHandle = NULL;
  802.     
  803.     error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
  804.     if ( error == noErr )
  805.     {
  806.         error = GetDesktopFileName(realVRefNum, desktopName);
  807.         if ( error == noErr )
  808.         {
  809.             savedResFile = CurResFile();
  810.         
  811.             /*
  812.             **    Open the 'Desktop' file in the root directory. (because
  813.             **    opening the resource file could preload unwanted resources,
  814.             **    bracket the call with SetResLoad(s))
  815.             */
  816.             SetResLoad(false);
  817.             dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
  818.             SetResLoad(true);
  819.         
  820.             if ( dfRefNum != -1 )
  821.             {
  822.                 /*
  823.                 **    Find the BNDL resource with the specified creator.
  824.                 */
  825.                 error = FindBundleGivenCreator(fileCreator, &theBndl);
  826.                 if ( error == noErr )
  827.                 {
  828.                     /* Lock the BNDL resource so it won't be purged when other resources are loaded */
  829.                     bndlState = HGetState((Handle)theBndl);
  830.                     HLock((Handle)theBndl);
  831.                     
  832.                     /* Find the 'FREF' BundleType record in the BNDL resource. */
  833.                     error = FindTypeInBundle(kFREFResType, theBndl, &theBundleType);
  834.                     if ( error == noErr )
  835.                     {
  836.                         /* Find the local ID in the 'FREF' resource with the specified fileType */
  837.                         error = GetLocalIDFromFREF(theBundleType, fileType, &iconLocalID);
  838.                         if ( error == noErr )
  839.                         {
  840.                             /* Find the 'ICN#' BundleType record in the BNDL resource. */
  841.                             error = FindTypeInBundle(kIconFamResType, theBndl, &theBundleType);
  842.                             if ( error == noErr )
  843.                             {
  844.                                 /* Find the icon's resource ID in the 'ICN#' BundleType record */
  845.                                 error = GetIconRsrcIDFromLocalID(theBundleType, iconLocalID, &iconRsrcID);
  846.                                 if ( error == noErr )
  847.                                 {
  848.                                     /* Map Desktop Manager icon type to resource type */
  849.                                     iconRsrcType = DTIconToResIcon(iconType);
  850.                                     
  851.                                     if ( iconRsrcType != (OSType)0 )
  852.                                     {
  853.                                         /* Load the icon */
  854.                                         returnIconHandle = Get1Resource(iconRsrcType, iconRsrcID);
  855.                                         if ( returnIconHandle != NULL )
  856.                                         {
  857.                                             /* Copy the resource handle, and return the copy */
  858.                                             HandToHand(&returnIconHandle);
  859.                                             if ( MemError() == noErr )
  860.                                             {
  861.                                                 *iconHandle = returnIconHandle;
  862.                                             }
  863.                                             else
  864.                                             {
  865.                                                 error = afpItemNotFound;
  866.                                             }
  867.                                         }
  868.                                         else
  869.                                         {
  870.                                             error = afpItemNotFound;
  871.                                         }
  872.                                     }
  873.                                 }
  874.                             }
  875.                         }
  876.                     }
  877.                     /* Restore the state of the BNDL resource */ 
  878.                     HSetState((Handle)theBndl, bndlState);
  879.                 }
  880.                 /* Restore the resource chain and close the Desktop file */
  881.                 UseResFile(savedResFile);
  882.                 CloseResFile(dfRefNum);
  883.             }
  884.             else
  885.             {
  886.                 error = ResError(); /* could not open Desktop file */
  887.             }
  888.         }
  889.         if ( (error != noErr) && (error != memFullErr) )
  890.         {
  891.             error = afpItemNotFound;    /* force an error we should return */
  892.         }
  893.     }
  894.     
  895.     return ( error );
  896. }
  897.  
  898. /*****************************************************************************/
  899.  
  900. pascal    OSErr    DTGetIcon(ConstStr255Param volName,
  901.                           short vRefNum,
  902.                           short iconType,
  903.                           OSType fileCreator,
  904.                           OSType fileType,
  905.                           Handle *iconHandle)
  906. {
  907.     OSErr error;
  908.     DTPBRec pb;
  909.     short dtRefNum;
  910.     Boolean newDTDatabase;
  911.     Size bufferSize;
  912.     
  913.     *iconHandle = NULL;
  914.     error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase);
  915.     if ( error == noErr )
  916.     {
  917.         /* there was a desktop database and it's now open */
  918.         
  919.         if ( !newDTDatabase )    /* don't bother to look in a new (empty) database */
  920.         {
  921.             /* get the buffer size for the requested icon type */
  922.             switch ( iconType )
  923.             {
  924.                 case kLargeIcon:
  925.                     bufferSize = kLargeIconSize;
  926.                     break;
  927.                 case kLarge4BitIcon:
  928.                     bufferSize = kLarge4BitIconSize;
  929.                     break;
  930.                 case kLarge8BitIcon:
  931.                     bufferSize = kLarge8BitIconSize;
  932.                     break;
  933.                 case kSmallIcon:
  934.                     bufferSize = kSmallIconSize;
  935.                     break;
  936.                 case kSmall4BitIcon:
  937.                     bufferSize = kSmall4BitIconSize;
  938.                     break;
  939.                 case kSmall8BitIcon:
  940.                     bufferSize = kSmall8BitIconSize;
  941.                     break;
  942.                 default:
  943.                     iconType = 0;
  944.                     bufferSize = 0;
  945.                     break;
  946.             }
  947.             if ( bufferSize != 0 )
  948.             {
  949.                 *iconHandle = NewHandle(bufferSize);
  950.                 if ( *iconHandle != NULL )
  951.                 {
  952.                     HLock(*iconHandle);
  953.         
  954.                     pb.ioDTRefNum = dtRefNum;
  955.                     pb.ioTagInfo = 0;
  956.                     pb.ioDTBuffer = **iconHandle;
  957.                     pb.ioDTReqCount = bufferSize;
  958.                     pb.ioIconType = iconType;
  959.                     pb.ioFileCreator = fileCreator;
  960.                     pb.ioFileType = fileType;
  961.                     error = PBDTGetIconSync(&pb);
  962.     
  963.                     HUnlock(*iconHandle);
  964.                     
  965.                     if ( error != noErr )
  966.                     {
  967.                         DisposeHandle(*iconHandle);    /* dispose of the allocated memory */
  968.                         *iconHandle = NULL;
  969.                     }
  970.                 }
  971.                 else
  972.                 {
  973.                     error = memFullErr;    /* handle could not be allocated */
  974.                 }
  975.             }
  976.             else
  977.             {
  978.                 error = paramErr;    /* unknown icon type requested */
  979.             }
  980.         }
  981.         else
  982.         {
  983.             error = afpItemNotFound;    /* the desktop database was empty - nothing to return */
  984.         }
  985.     }
  986.     else
  987.     {
  988.         /* There is no desktop database - try the Desktop file */
  989.         
  990.         error = GetIconFromDesktopFile(volName, vRefNum, iconType,
  991.                                         fileCreator, fileType, iconHandle);
  992.     }
  993.     
  994.     return ( error );
  995. }
  996.  
  997. /*****************************************************************************/
  998.  
  999. pascal    OSErr    DTSetComment(short vRefNum,
  1000.                              long dirID,
  1001.                              ConstStr255Param name,
  1002.                              ConstStr255Param comment)
  1003. {
  1004.     DTPBRec pb;
  1005.     OSErr error;
  1006.     short dtRefNum;
  1007.     Boolean newDTDatabase;
  1008.  
  1009.     error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase);
  1010.     if ( error == noErr )
  1011.     {
  1012.         pb.ioDTRefNum = dtRefNum;
  1013.         pb.ioNamePtr = (StringPtr)name;
  1014.         pb.ioDirID = dirID;
  1015.         pb.ioDTBuffer = (Ptr)&comment[1];
  1016.         /* Truncate the comment to 200 characters just in case */
  1017.         /* some file system doesn't range check */
  1018.         if ( comment[0] <= 200 )
  1019.         {
  1020.             pb.ioDTReqCount = comment[0];
  1021.         }
  1022.         else
  1023.         {
  1024.             pb.ioDTReqCount = 200;
  1025.         }
  1026.         error = PBDTSetCommentSync(&pb);
  1027.     }
  1028.     return (error);
  1029. }
  1030.  
  1031. /*****************************************************************************/
  1032.  
  1033. pascal    OSErr    FSpDTSetComment(const FSSpec *spec,
  1034.                               ConstStr255Param comment)
  1035. {
  1036.     return (DTSetComment(spec->vRefNum, spec->parID, spec->name, comment));
  1037. }
  1038.  
  1039. /*****************************************************************************/
  1040.  
  1041. /*
  1042. **    GetCommentID
  1043. **
  1044. **    Get the comment ID number for the Desktop file's 'FCMT' resource ID from
  1045. **    the file or folders fdComment (frComment) field.
  1046. */
  1047. static    OSErr    GetCommentID(short vRefNum,
  1048.                              long dirID,
  1049.                              ConstStr255Param name,
  1050.                              short *commentID)
  1051. {
  1052.     CInfoPBRec pb;
  1053.     OSErr error;
  1054.  
  1055.     error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
  1056.     *commentID = pb.hFileInfo.ioFlXFndrInfo.fdComment;
  1057.     return ( error );
  1058. }
  1059.  
  1060. /*****************************************************************************/
  1061.  
  1062. /*
  1063. **    GetCommentFromDesktopFile
  1064. **
  1065. **    Get a file or directory's Finder comment field (if any) from the
  1066. **    Desktop file's 'FCMT' resources.
  1067. */
  1068. static    OSErr    GetCommentFromDesktopFile(short vRefNum,
  1069.                                           long dirID,
  1070.                                           ConstStr255Param name,
  1071.                                           Str255 comment)
  1072. {
  1073.     OSErr error;
  1074.     short commentID;
  1075.     short realVRefNum;
  1076.     Str255 desktopName;
  1077.     short savedResFile;
  1078.     short dfRefNum;
  1079.     StringHandle commentHandle;
  1080.     
  1081.     /* Get the comment ID number */
  1082.     error = GetCommentID(vRefNum, dirID, name, &commentID);
  1083.     if ( error == noErr )
  1084.     {
  1085.         if ( commentID != 0 )    /* commentID == 0 means there's no comment */
  1086.         {
  1087.             error = DetermineVRefNum(name, vRefNum, &realVRefNum);
  1088.             if ( error == noErr )
  1089.             {
  1090.                 error = GetDesktopFileName(realVRefNum, desktopName);
  1091.                 if ( error == noErr )
  1092.                 {
  1093.                     savedResFile = CurResFile();
  1094.                     /*
  1095.                     **    Open the 'Desktop' file in the root directory. (because
  1096.                     **    opening the resource file could preload unwanted resources,
  1097.                     **    bracket the call with SetResLoad(s))
  1098.                     */
  1099.                     SetResLoad(false);
  1100.                     dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
  1101.                     SetResLoad(true);
  1102.                     
  1103.                     if ( dfRefNum != -1)
  1104.                     {
  1105.                         /* Get the comment resource */
  1106.                         commentHandle = (StringHandle)Get1Resource(kFCMTResType,commentID);
  1107.                         if ( commentHandle != NULL )
  1108.                         {
  1109.                             if ( InlineGetHandleSize((Handle)commentHandle) > 0 )
  1110.                             {
  1111.                                 BlockMoveData(*commentHandle, comment, *commentHandle[0] + 1);
  1112.                             }
  1113.                             else
  1114.                             {
  1115.                                 error = afpItemNotFound;    /* no comment available */
  1116.                             }
  1117.                         }
  1118.                         else
  1119.                         {
  1120.                             error = afpItemNotFound;    /* no comment available */
  1121.                         }
  1122.                         
  1123.                         /* restore the resource chain and close the Desktop file */
  1124.                         UseResFile(savedResFile);
  1125.                         CloseResFile(dfRefNum);
  1126.                     }
  1127.                     else
  1128.                     {
  1129.                         error = afpItemNotFound;
  1130.                     }
  1131.                 }
  1132.                 else
  1133.                 {
  1134.                     error = afpItemNotFound;
  1135.                 }
  1136.             }
  1137.         }
  1138.         else
  1139.         {
  1140.             error = afpItemNotFound;    /* no comment available */
  1141.         }
  1142.     }
  1143.     
  1144.     return ( error );
  1145. }
  1146.  
  1147. /*****************************************************************************/
  1148.  
  1149. pascal    OSErr    DTGetComment(short vRefNum,
  1150.                              long dirID,
  1151.                              ConstStr255Param name,
  1152.                              Str255 comment)
  1153. {
  1154.     DTPBRec pb;
  1155.     OSErr error;
  1156.     short dtRefNum;
  1157.     Boolean newDTDatabase;
  1158.  
  1159.     if (comment != NULL)
  1160.     {
  1161.         comment[0] = 0;    /* return nothing by default */
  1162.         
  1163.         /* attempt to open the desktop database */
  1164.         error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase);
  1165.         if ( error == noErr )
  1166.         {
  1167.             /* There was a desktop database and it's now open */
  1168.             
  1169.             if ( !newDTDatabase )
  1170.             {
  1171.                 pb.ioDTRefNum = dtRefNum;
  1172.                 pb.ioNamePtr = (StringPtr)name;
  1173.                 pb.ioDirID = dirID;
  1174.                 pb.ioDTBuffer = (Ptr)&comment[1];
  1175.                 /*
  1176.                 **    IMPORTANT NOTE #1: Inside Macintosh says that comments
  1177.                 **    are up to 200 characters. While that may be correct for
  1178.                 **    the HFS file system's Desktop Manager, other file
  1179.                 **    systems (such as Apple Photo Access) return up to
  1180.                 **    255 characters. Make sure the comment buffer is a Str255
  1181.                 **    or you'll regret it.
  1182.                 **
  1183.                 **    IMPORTANT NOTE #2: Although Inside Macintosh doesn't
  1184.                 **    mention it, ioDTReqCount is a input field to
  1185.                 **    PBDTGetCommentSync. Some file systems (like HFS) ignore
  1186.                 **    ioDTReqCount and always return the full comment --
  1187.                 **    others (like AppleShare) respect ioDTReqCount and only
  1188.                 **    return up to ioDTReqCount characters of the comment.
  1189.                 */
  1190.                 pb.ioDTReqCount = sizeof(Str255) - 1;
  1191.                 error = PBDTGetCommentSync(&pb);
  1192.                 if (error == noErr)
  1193.                 {
  1194.                     comment[0] = (unsigned char)pb.ioDTActCount;
  1195.                 }
  1196.             }
  1197.         }
  1198.         else
  1199.         {
  1200.             /* There is no desktop database - try the Desktop file */
  1201.             error = GetCommentFromDesktopFile(vRefNum, dirID, name, comment);
  1202.             if ( error != noErr )
  1203.             {
  1204.                 error = afpItemNotFound;    /* return an expected error */
  1205.             }
  1206.         }
  1207.     }
  1208.     else
  1209.     {
  1210.         error = paramErr;
  1211.     }
  1212.     
  1213.     return (error);
  1214. }
  1215.  
  1216. /*****************************************************************************/
  1217.  
  1218. pascal    OSErr    FSpDTGetComment(const FSSpec *spec,
  1219.                               Str255 comment)
  1220. {
  1221.     return (DTGetComment(spec->vRefNum, spec->parID, spec->name, comment));
  1222. }
  1223.  
  1224. /*****************************************************************************/
  1225.  
  1226. pascal    OSErr    DTCopyComment(short srcVRefNum,
  1227.                               long srcDirID,
  1228.                               ConstStr255Param srcName,
  1229.                               short dstVRefNum,
  1230.                               long dstDirID,
  1231.                               ConstStr255Param dstName)
  1232. /* The destination volume must support the Desktop Manager for this to work */
  1233. {
  1234.     OSErr error;
  1235.     Str255 comment;
  1236.  
  1237.     error = DTGetComment(srcVRefNum, srcDirID, srcName, comment);
  1238.     if ( (error == noErr) && (comment[0] > 0) )
  1239.     {
  1240.         error = DTSetComment(dstVRefNum, dstDirID, dstName, comment);
  1241.     }
  1242.     return (error);
  1243. }
  1244.  
  1245. /*****************************************************************************/
  1246.  
  1247. pascal    OSErr    FSpDTCopyComment(const FSSpec *srcSpec,
  1248.                                const FSSpec *dstSpec)
  1249. /* The destination volume must support the Desktop Manager for this to work */
  1250. {
  1251.     return (DTCopyComment(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
  1252.                         dstSpec->vRefNum, dstSpec->parID, dstSpec->name));
  1253. }
  1254.  
  1255. /*****************************************************************************/
  1256.